﻿using NetCore.Dtos;
using NetCore.Services;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;

namespace NetCore.EnumServices
{
    /// <summary>
    /// 构造枚举类型上下文 
    /// </summary>
    public class EnumContext
    {
        #region 静态

        /// <summary>
        /// 枚举类所在程序集名称 默认值
        /// </summary>
        public static string EnumAssemblyName = CustomConfigurationManager.EnumAssemblyName;

        /// <summary>
        /// 标识枚举类型所在程序集的dll文件名
        /// 提供给上端变更枚举类型所在程序集 上端可以读配置文件
        /// </summary>
        /// <param name="func">初始化模块时传递的委托</param>
        public static void SetAssembly(Func<string, string> func)
        {
            EnumAssemblyName = func.Invoke("Abp:EnumAssemblyName") ?? CustomConfigurationManager.EnumAssemblyName;
        }

        /// <summary>
        /// 静态实例
        /// </summary>
        public static EnumContext Instance => new EnumContext();

        #endregion

        #region 初始化枚举类型字典

        /// <summary>
        /// 枚举项key itemName itemDescription itemKeyString 集合
        /// </summary>
        private Dictionary<Type, List<EnumTypeItemDto>> EnumTypeItemKeyNameDescriptions { get; set; }

        private EnumContext()
        {
            EnumTypeItemKeyNameDescriptions = new Dictionary<Type, List<EnumTypeItemDto>>();
            //EnumAssemblyName = CustomConfigurationManager.EnumAssemblyName;
        }

        /// <summary>
        /// 初始化枚举类型元数据
        /// </summary>
        /// <param name="type"></param>
        private void InitEnumTypeMetaData(Type type)
        {
            if (EnumTypeItemKeyNameDescriptions.ContainsKey(type))
                return;
            var typeItemKeyValueNameDescriptions = new List<EnumTypeItemDto>();
            var values = Enum.GetValues(type);
            foreach (var value in values)
            {
                var key = (int)value;//EnumValue 枚举项的整型值
                var name = value.ToString();//EnumValueName 枚举项字符串
                var description = type.GetField(name).GetCustomAttribute<DescriptionAttribute>();//枚举项描述
                var enumDto = new EnumTypeItemDto(key, name, description?.Description);
                typeItemKeyValueNameDescriptions.Add(enumDto);
            }
            EnumTypeItemKeyNameDescriptions.Add(type, typeItemKeyValueNameDescriptions);
        }

        /// <summary>
        /// 确保枚举所有项已加载
        /// </summary>
        /// <param name="type"></param>
        private void LoadEnumTypeContext(Type type)
        {
            if (!EnumTypeItemKeyNameDescriptions.ContainsKey(type))
            {
                InitEnumTypeMetaData(type);
            }
        }

        #endregion

        #region 列出系统所有枚举列表及根据名称后去枚举类型方法
        /// <summary>
        /// 返回系统所有枚举类型 类型名称->类型描述/全名
        /// </summary>
        /// <returns></returns>
        public IEnumerable<KeyValueDto<string, string>> GetAllEnumTypeNames()
        {
            return Assembly
                .Load(EnumAssemblyName)
                .GetTypes().Where(l => l.IsEnum)//可空枚举呢
                .Select(m => new KeyValueDto<string, string>(m.Name, m.GetCustomAttribute<DescriptionAttribute>()?.Description ?? m.Name));
        }
        /// <summary>
        /// 根据枚举类名获取枚举类型的类型：typeOf(EnumType)
        /// </summary>
        /// <param name="input">枚举类名</param>
        /// <returns></returns>
        public Type GeTypeByName(InputNameDto input)
        {
            return GeTypeByName(input.Name);
        }
        /// <summary>
        /// 根据枚举类名获取枚举类型的类型：typeOf(EnumType)
        /// </summary>
        /// <param name="name">枚举类名</param>
        /// <returns></returns>
        public Type GeTypeByName(string name)
        {
            name = name?.ToUpper();
            return Assembly
                .Load(EnumAssemblyName)
                .GetTypes().Where(m => m.IsEnum)
                .FirstOrDefault(n => n.Name.ToUpper() == name);//Equals(name, StringComparison.CurrentCultureIgnoreCase)
        }
        #endregion


        #region 获取 枚举项集合 EnumTypeItemKeyNameDescriptions 返回给上端可组织任意返回数据格式
        /// <summary>
        /// 获取 枚举项集合 EnumTypeItemKeyNameDescriptions
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public List<EnumTypeItemDto> GetEnumTypeItemKeyNameDescriptions(InputNameDto input)
        {
            var type = GeTypeByName(input);
            if (!EnumTypeItemKeyNameDescriptions.ContainsKey(type))
            {
                InitEnumTypeMetaData(type);
            }
            return EnumTypeItemKeyNameDescriptions[type];
        }
        /// <summary>
        /// 获取 枚举项集合 EnumTypeItemKeyNameDescriptions
        /// </summary>
        /// <param name="name">枚举类名称</param>
        /// <returns></returns>
        public List<EnumTypeItemDto> GetEnumTypeItemKeyNameDescriptions(string name)
        {
            var type = GeTypeByName(name);
            if (!EnumTypeItemKeyNameDescriptions.ContainsKey(type))
            {
                InitEnumTypeMetaData(type);
            }
            return EnumTypeItemKeyNameDescriptions[type];
        }
        #endregion


        #region 以下方法仅参考 注释

        #region 获取枚举类型 EnumKey(枚举整型值)-->EnumItemString(枚举值字符串)-->EnumKey(枚举整型值)

        /// <summary>
        /// 获取枚举项值ItemKey-->枚举值字符串ItemStringName [ 0->"male",10->"female"]列表
        /// </summary>
        /// <param name="input">含枚举类名反射-->枚举类型Type->TypeOf(EnumType)</param>
        /// <returns></returns>
        public List<KeyValueDto> GetEnumItemKeyToItemStringName(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].Select(n => new KeyValueDto(n.ItemKey, n.ItemStringName)).ToList();
        }

        /// <summary>
        /// 获取枚举项值字符串ItemKeyString-->枚举值字符串ItemStringName ["0"->"male","10"->"female"]列表
        /// </summary>
        /// <param name="input">含枚举类名反射-->枚举类型Type->TypeOf(EnumType)</param>
        /// <returns></returns>
        public List<KeyValueDto<string, string>> GetEnumItemKeyStringToItemStringName(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].Select(n => new KeyValueDto<string, string>(n.ItemKeyString, n.ItemStringName)).ToList();
        }

        /// <summary>
        /// 获取枚举项(字符串)ItemStringName--ItemKey(枚举整型值)  ["male"->0,"female"->10]列表
        /// </summary>
        /// <param name="input">含枚举类名反射-->枚举类型Type->TypeOf(EnumType)</param>
        /// <returns></returns>
        public List<KeyValueDto<string, int>> GetEnumItemStringNameToItemKey(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].Select(n => new KeyValueDto<string, int>(n.ItemStringName, n.ItemKey)).ToList();
        }
        /// <summary>
        /// 获取枚举项(字符串)ItemStringName--ItemKeyString(枚举整型值字符串)   ["male"->"0","female"->"10"]列表
        /// </summary>
        /// <param name="input">含枚举类名反射-->枚举类型Type->TypeOf(EnumType)</param>
        /// <returns></returns>
        public List<KeyValueDto<string, string>> GetEnumItemStringNameToItemKeyString(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].Select(n => new KeyValueDto<string, string>(n.ItemStringName, n.ItemKeyString)).ToList();
        }
        #endregion

        #region 获取枚举类型 EnumKey(枚举整型值)-->EnumItemDescription(描述)/EnumItemString(枚举值字符串)-->EnumKey(枚举整型值)

        /// <summary>
        /// 获取枚举项值ItemKey-->枚举值字符串ItemDescription  [ 0->"男",10->"女"]列表
        /// </summary>
        /// <param name="input">含枚举类名反射-->枚举类型Type->TypeOf(EnumType)</param>
        /// <returns></returns>
        public List<KeyValueDto> GetEnumItemKeyToItemDescription(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].Select(n => new KeyValueDto(n.ItemKey, n.ItemDescription)).ToList();
        }
        /// <summary>
        /// 获取枚举项值字符串ItemKeyString-->枚举值字符串ItemDescription [ "0"->"男","10"->"女"]列表
        /// </summary>
        /// <param name="input">含枚举类名反射-->枚举类型Type->TypeOf(EnumType)</param>
        /// <returns></returns>
        public List<KeyValueDto<string, string>> GetEnumKeyStringToItemDescription(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].Select(n => new KeyValueDto<string, string>(n.ItemKeyString, n.ItemDescription)).ToList();
        }

        /// <summary>
        /// 获取枚举项描述ItemDescription--ItemKey(枚举整型值) [ "男->0,"女"->10]列表
        /// </summary>
        /// <param name="input">含枚举类名反射-->枚举类型Type->TypeOf(EnumType)</param>
        /// <returns></returns>
        public List<KeyValueDto<string, int>> GetEnumItemDescriptionToItemKey(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].Select(n => new KeyValueDto<string, int>(n.ItemDescription, n.ItemKey)).ToList();
        }
        /// <summary>
        /// 获取枚举项描述ItemDescription--ItemKeyString(枚举整型值字符串) [ "男->"0","女"->"10"]列表
        /// </summary>
        /// <param name="input">含枚举类名反射-->枚举类型Type->TypeOf(EnumType)</param>
        /// <returns></returns>
        public List<KeyValueDto<string, string>> GetEnumItemDescriptionToItemKeyString(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].Select(n => new KeyValueDto<string, string>(n.ItemDescription, n.ItemKeyString)).ToList();
        }
        #endregion

        #region 获取枚举类型 EnumKey(枚举整型值)-->获取枚举类型 EnumItemDescription/EnumItemString(描述/枚举值字符串)-->EnumKey(枚举整型值)

        /// <summary>
        /// 获取枚举项值ItemKey--> ItemDescriptionOrName(描述/枚举值字符串) [ 0->"男??male"->,10->"女??female"]列表
        /// </summary>
        /// <param name="input">含枚举类名反射-->枚举类型Type->TypeOf(EnumType)</param>
        /// <returns></returns>
        public List<KeyValueDto> GetEnumItemKeyToItemDescriptionOrName(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].Select(n => new KeyValueDto(n.ItemKey, n.ItemDescriptionOrName)).ToList();
        }
        /// <summary>
        /// 获取枚举项值ItemKeyString-->ItemDescriptionOrName(描述/枚举值字符串) [ "0"->"男??male"->,"10"->"女??female"]列表
        /// </summary>
        /// <param name="input">含枚举类名反射-->枚举类型Type->TypeOf(EnumType)</param>
        /// <returns></returns>
        public List<KeyValueDto<string, string>> GetEnumItemKeyStringToItemDescriptionOrName(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].Select(n => new KeyValueDto<string, string>(n.ItemKeyString, n.ItemDescriptionOrName)).ToList();
        }

        /// <summary>
        /// ItemDescriptionOrName(描述/枚举值字符串)-->ItemKey(枚举整型值) [ "男??male"->0,"女??female"=>10]列表
        /// </summary>
        /// <param name="input">含枚举类名反射-->枚举类型Type->TypeOf(EnumType)</param>
        /// <returns></returns>
        public List<KeyValueDto<string, int>> GetEnumItemDescriptionOrNameToItemKey(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].Select(n => new KeyValueDto<string, int>(n.ItemDescriptionOrName, n.ItemKey)).ToList();
        }
        /// <summary>
        /// ItemDescriptionOrName(描述/枚举值字符串)-->ItemKeyString(枚举整型值字符串) [ "男??male"->"0","女??female"->"10"]列表
        /// </summary>
        /// <param name="input">含枚举类名反射-->枚举类型Type->TypeOf(EnumType)</param>
        /// <returns></returns>
        public List<KeyValueDto<string, string>> GetEnumItemDescriptionOrNameToItemKeyString(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].Select(n => new KeyValueDto<string, string>(n.ItemDescriptionOrName, n.ItemKeyString)).ToList();
        }
        #endregion

        #region 获取枚举类型 EnumItemString(枚举值字符串)-->EnumItemDescription(描述)-->EnumItemString(枚举值字符串)

        /// <summary>
        /// 获取枚举项ItemStringName--> ItemDescription(描述) [ "male"->"男","female"->"女"]列表
        /// </summary>
        /// <param name="input">含枚举类名反射-->枚举类型Type->TypeOf(EnumType)</param>
        /// <returns></returns>
        public List<KeyValueDto<string, string>> GetEnumItemStringNameToItemDescription(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].Select(n => new KeyValueDto<string, string>(n.ItemStringName, n.ItemDescription)).ToList();
        }
        /// <summary>
        /// 获取枚举项描述ItemDescription--> ItemStringName(枚举项字符串)  [ "男"->"male","女"->"female"]列表
        /// </summary>
        /// <param name="input">含枚举类名反射-->枚举类型Type->TypeOf(EnumType)</param>
        /// <returns></returns>
        public List<KeyValueDto<string, string>> GetEnumItemDescriptionToItemStringName(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].Select(n => new KeyValueDto<string, string>(n.ItemDescription, n.ItemStringName)).ToList();
        }


        #endregion

        #region 获取枚举类型字典方法  使用以上返回key-value的方法代替

        /// <summary>
        /// 根据类型获取枚举类型描述/名称 字典 GetEnumDescriptionOrNameDictionaryByEnumType
        /// </summary>
        /// <param name="type">枚举类型type</param>
        /// <returns>枚举类型描述/名称 字典</returns>
        public Dictionary<int, string> GetEnumTypeItemKeyDescriptionOrNameDictionary(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].ToDictionary(m => m.ItemKey, n => n.ItemStringName);
        }

        /// <summary>
        /// 根据类型获取枚举类型描述/名称 字典 GetEnumDescriptionDictionaryByEnumType
        /// </summary>
        /// <param name="type">枚举类型type</param>
        /// <returns>枚举类型描述/名称 字典</returns>
        public Dictionary<int, string> GetEnumTypeItemKeyDescriptionDictionary(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].ToDictionary(m => m.ItemKey, n => n.ItemDescription);
        }
        /// <summary>
        /// 根据类型获取枚举类型描述/名称 字典 GetEnumDescriptionDictionaryByEnumType
        /// </summary>
        /// <param name="type">枚举类型type</param>
        /// <returns>枚举类型值 字典</returns>
        public Dictionary<int, string> GetEnumTypeItemKeyStringNameDictionary(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].ToDictionary(m => m.ItemKey, n => n.ItemDescriptionOrName);
        }

        /// <summary>
        /// 根据类型获取枚举类型名称->描述 字典 GetEnumDescriptionDictionaryByEnumType
        /// </summary>
        /// <param name="type">枚举类型type</param>
        /// <returns>枚举类型值 字典</returns>
        public Dictionary<string, string> GetEnumTypeItemStringNameDescriptionDictionary(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].ToDictionary(m => m.ItemStringName, n => n.ItemDescription);
        }
        /// <summary>
        /// 根据类型获取枚举类型描述->名称 字典 GetEnumDescriptionDictionaryByEnumType
        /// </summary>
        /// <param name="type">枚举类型type</param>
        /// <returns>枚举类型值 字典</returns>
        public Dictionary<string, string> GetEnumTypeItemDescriptionItemStringNameDictionary(InputNameDto input)
        {
            var type = GeTypeByName(input);
            LoadEnumTypeContext(type);
            return EnumTypeItemKeyNameDescriptions[type].ToDictionary(m => m.ItemDescription, n => n.ItemStringName);
        }

        #endregion

        #endregion



    }
}
